using System;
using EarthGeospatial;
using Geometry;
using Unity.Mathematics;

namespace Cesium3DTilesSelection
{
    public class ViewState
    {
        double3 _position;
        double3 _direction;
        double3 _up;
        double2 _viewportSize;
        double _horizontalFieldOfView;
        double _verticalFieldOfView;

        double _sseDenominator;
        Cartographic? _positionCartographic;

        public CullingVolume _cullingVolume;

        public static ViewState create(
                     double3 position,
                     double3 direction,
                     double3 up,
                      double2 viewportSize,
                      double horizontalFieldOfView,
                      double verticalFieldOfView)
        {
            return create(position, direction, up, viewportSize, horizontalFieldOfView, verticalFieldOfView, Ellipsoid.WGS84);
        }
        public static ViewState create(
                     double3 position,
                     double3 direction,
                     double3 up,
                      double2 viewportSize,
                      double horizontalFieldOfView,
                      double verticalFieldOfView,
                      Ellipsoid ellipsoid)
        {
            return new ViewState(
      position,
      direction,
      up,
      viewportSize,
      horizontalFieldOfView,
      verticalFieldOfView,
      ellipsoid,
      ellipsoid.cartesianToCartographic(position).Value);
        }

        public ViewState(
                double3 position,
                double3 direction,
                double3 up,
                 double2 viewportSize,
                 double horizontalFieldOfView,
                 double verticalFieldOfView,
                  Ellipsoid ellipsoid,
                  Cartographic positionCartographic)
        {

            _position = position;
            _direction = direction;
            _up = up;
            _viewportSize = viewportSize;
            _horizontalFieldOfView = horizontalFieldOfView;
            _verticalFieldOfView = verticalFieldOfView;
            _sseDenominator = (2.0 * math.tan(0.5 * verticalFieldOfView));
            _positionCartographic = positionCartographic;
            _cullingVolume = CullingVolume.createCullingVolume(
                position,
                direction,
                up,
                horizontalFieldOfView,
                verticalFieldOfView);
        }

        public double computeDistanceSquaredToBoundingVolume(BoundingVolume boundingVolume)
        {
            if (boundingVolume.pOrientedBBox)
            {
                return boundingVolume.pOrientedBBox.computeDistanceSquaredToPosition(_position);
            }
            else if (boundingVolume.pBoundingRegion)
            {
                return boundingVolume.pBoundingRegion.computeDistanceSquaredToPosition(_position);
            }
            else if (boundingVolume.pBoundingSphere)
            {
                return boundingVolume.pBoundingSphere.computeDistanceSquaredToPosition(_position);
            }
            else if (boundingVolume.pLooseRegion)
            {
                if (_positionCartographic.HasValue)
                    return boundingVolume.pLooseRegion.computeConservativeDistanceSquaredToPosition(_positionCartographic.Value, _position);
                return boundingVolume.pLooseRegion.computeConservativeDistanceSquaredToPosition(_position);
            }
            else
            {
                return 0;
            }
        }

        internal double3 getPosition()
        {
            return _position;
        }

        internal double3 getDirection()
        {
            return _direction;
        }

        public double computeScreenSpaceError(double geometricError, double distance)
        {
            distance = math.max(distance, 1e-7);
            double sseDenominator = this._sseDenominator;
            return (geometricError * this._viewportSize.y) / (distance * sseDenominator);
        }

        public Cartographic? getPositionCartographic()
        {
            return this._positionCartographic;
        }

        public bool isBoundingVolumeVisible(BoundingVolume boundingVolume)
        {
            return isBoundingVolumeVisible(boundingVolume, _cullingVolume);
        }

        public static bool isBoundingVolumeVisible(BoundingVolume boundingVolume, CullingVolume cullingVolume)
        {
            CullingResult left = boundingVolume.intersectPlane(cullingVolume.leftPlane);
            if (left == CullingResult.Outside)
            {
                return false;
            }

            CullingResult right = boundingVolume.intersectPlane(cullingVolume.rightPlane);
            if (right == CullingResult.Outside)
            {
                return false;
            }

            CullingResult top = boundingVolume.intersectPlane(cullingVolume.topPlane);
            if (top == CullingResult.Outside)
            {
                return false;
            }

            CullingResult bottom = boundingVolume.intersectPlane(cullingVolume.bottomPlane);
            if (bottom == CullingResult.Outside)
            {
                return false;
            }

            return true;
        }
    }
}